package de.blitzcoder.collide.gui;

import de.blitzcoder.collide.docs.Documentation;
import de.blitzcoder.collide.docs.Module;
import de.blitzcoder.collide.docs.Scope;
import de.blitzcoder.collide.engine.parser.CodeObject;
import de.blitzcoder.collide.gui.textarea.CollIDETextArea;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.SwingUtilities;
import org.gjt.sp.jedit.buffer.BufferListener;
import org.gjt.sp.jedit.buffer.JEditBuffer;

/**
 *
 * @author blitzcoder
 */
public class BlitzMaxCorrectionBufferListener implements BufferListener {

    private static String[] keywords;
    private static Pattern findKeywords = Pattern.compile("[a-zA-Z_]\\w*(?=\\W+)"); // Findet keywords im Code
    private static HashMap<String, String> extensions;

    private CollIDETextArea textArea;

    static {

        // Read some keywords
        LinkedList<String> keywords = new LinkedList<String>();

        // Add the standard Bmax keywords
        ListIterator<String> keywordIt = Documentation.keywordList.listIterator();
        while (keywordIt.hasNext()) {
            keywords.addLast(keywordIt.next());
        }

        // Add all keywords from docs
        ListIterator<Scope> scopeIt = Documentation.scopeList.listIterator();
        while (scopeIt.hasNext()) {
            Scope scope = scopeIt.next();
            keywords.addLast(scope.getName());
            ListIterator<Module> moduleIt = scope.iterateModules();
            while (moduleIt.hasNext()) {
                Module module = moduleIt.next();
                keywords.addLast(module.getName());
                ListIterator<CodeObject> contentIt = module.iterateContent();
                while (contentIt.hasNext()) {
                    CodeObject obj = contentIt.next();
                    keywords.addLast(obj.getName());
                }
            }
        }

        BlitzMaxCorrectionBufferListener.keywords = keywords.toArray(new String[]{});

        extensions = new HashMap<String, String>();
        extensions.put("function", "EndFunction");
        extensions.put("method", "EndMethod");
        extensions.put("type", "EndType");
        extensions.put("extern", "EndExtern");
        extensions.put("if", "EndIf");
        extensions.put("else", "");
        extensions.put("elseif", "");
        extensions.put("select", "EndSelect");
        extensions.put("while", "Wend");
        extensions.put("for", "Next");
        extensions.put("repeat", "Until ");

    }

    public BlitzMaxCorrectionBufferListener(CollIDETextArea textArea) {
        this.textArea = textArea;
    }

    @Override
    public void contentInserted(final JEditBuffer buf, final int startLine, int offset, int numLines, int length) {

        // Groß-/Kleinschreibkorrektur für BlitzMax-Codes
        int startOffset = buf.getLineStartOffset(startLine);
        int endOffset = buf.getLineEndOffset(startLine+numLines);
        if (endOffset >= buf.getLength()) // Für die erste Zeile
            endOffset--;
        String text = buf.getText(startOffset, endOffset-startOffset);

        Matcher m = findKeywords.matcher(text);
        buf.beginCompoundEdit();
        while (m.find()) {
            
            for (String keyword:keywords) {
                if (keyword.equalsIgnoreCase(m.group()) && !keyword.equals(m.group())) { // Nur Case-Sensetive-Unterschiede
                    buf.remove(startOffset + m.start(), keyword.length());
                    buf.insert(startOffset + m.start(), keyword);
                    break;
                }
            }

        }
        buf.endCompoundEdit();

        // Nur erweitern wenn enter gedrückt wurde (neue zeile eingefügt)
        if (buf.getText(offset, 1).equals("\n")) {

            String line = buf.getLineText(startLine).replace(" ", "").replace("\t", "").toLowerCase();
            Iterator<String> it = extensions.keySet().iterator();
            while (it.hasNext()) {
                String ext = it.next();
                if (line.startsWith(ext)) {
                    
                    // Nur erweitern, wenn die nächste Zeile im Dokument nicht schon weiter eingerückt ist
                    int[] whitespaces = new int[] {9, 32}; // Space and Tab
                    if (startLine+1 < buf.getLineCount() || buf.getCurrentIndentForLine(startLine, whitespaces) >= buf.getCurrentIndentForLine(startLine+2, whitespaces)) {
                        buf.beginCompoundEdit();
                        String indent = getIndent(buf.getLineText(startLine));
                        buf.insert(offset+1, indent+"\n"+indent+extensions.get(ext));
                        SwingUtilities.invokeLater(new Runnable() {
                            @Override
                            public void run() {
                                buf.shiftIndentRight(new int[] {startLine+1});
                            }
                        });
                        textArea.setCaretPosition(buf.getLineEndOffset(startLine+1)-1);
                        buf.endCompoundEdit();
                    }

                    break;
                }
            }

        }

    }

    private int getIndentWidth(String line) {
        char[] c = line.toCharArray();
        int width = 0;
        for (int i = 0; i < c.length; i++) {
            if (c[i] == 9)
                width+=textArea.getBuffer().getIndentSize();
            else if (c[i] == 32)
                width++;
            else
                break;
        }
        return width;
    }

    private String getIndent(String text) {
        char[] c = text.toCharArray();
        int width = 0;
        for (int i = 0; i < c.length; i++) {
            if (c[i] != 9 && c[i] != 32) // Space or Tab
                break;
            width++;
        }
        return text.substring(0, width);
    }

    public void foldLevelChanged(JEditBuffer arg0, int arg1, int arg2) {}
    public void contentRemoved(JEditBuffer arg0, int arg1, int arg2, int arg3, int arg4) {}
    public void preContentInserted(JEditBuffer arg0, int arg1, int arg2, int arg3, int arg4) {}
    public void preContentRemoved(JEditBuffer arg0, int arg1, int arg2, int arg3, int arg4) {}
    public void transactionComplete(JEditBuffer arg0) {}
    public void foldHandlerChanged(JEditBuffer arg0) {}
    public void bufferLoaded(JEditBuffer arg0) {}
}
